package org.valkyrie.gwt.bean.client;

import org.valkyrie.gwt.bean.dev.generator.BeanManagerGenerator;

import com.google.gwt.core.client.GWT;

/**
 * An {@link BeanManager} is building by the {@link BeanManagerGenerator} for a
 * {@link ReflectedBean}. <br/>
 * This generated class give access to a bean properties via a kind of
 * reflection.<br/>
 * <br/>
 * For calling properties by reflection, use this code :<br/>
 * <br/>
 * 
 * <code>
 * {@link BeanManager}<MyReflectedBean> manager = {@link GWT}.create(MyReflectedBean.class);<br/>
 * MyReflectedBean <b>aReflectedBeanObject</b> = new MyReflectedBean();<br/>
 * manager.setBean(<b>aReflectedBeanObject</b>);<br/><br/>
 * (You can now used your properties)<br/>
 * manager.set("myProperty", "Barack Obama");<br/>
 * manager.get("myProperty");<br/>
 * </code> <br/>
 * 
 * In this example, all modifications will be do on <b>aReflectedBeanObject</b>
 * 
 * 
 * @author Pierre
 * @param <B>
 * 
 */
public abstract class BeanManager<B extends ReflectedBean> {

	private B bean;

	/**
	 * Get the managed bean
	 * 
	 * @return the bean
	 */
	public B getBean() {
		return bean;
	}

	/**
	 * Set the managed bean
	 * 
	 * @param bean
	 */
	public void setBean(B bean) {
		this.bean = bean;
	}

	/**
	 * Get the value of the specified property on the managed bean.
	 * 
	 * @param <T>
	 *            the property type
	 * @param property
	 *            the property name
	 * @return the property value
	 * @throws ReflectionException
	 */
	@SuppressWarnings("unchecked")
	public <T extends Object> T get(String property) throws ReflectionException {
		checkPropertyName(property);
		checkBean();
		try {
			return (T) invokeGetter(bean, property);
		} catch (ReflectionException e) {
			throw e;
		} catch (Exception e) {
			throw new ReflectionException(e);
		}
	}

	/**
	 * Set the value of the specified property on the managed bean.
	 * 
	 * @param <T>
	 *            the property type
	 * @param property
	 *            the property name
	 * @param value
	 *            the property value
	 * @throws ReflectionException
	 */
	public <T extends Object> void set(String property, T value)
			throws ReflectionException {
		checkPropertyName(property);
		checkBean();
		try {
			invokeSetter(bean, property, value);
		} catch (ReflectionException e) {
			throw e;
		} catch (Exception e) {
			throw new ReflectionException(e);
		}
	}

	private void checkBean() throws ReflectionException {
		if (bean == null)
			throw new ReflectionException(
					"Cannot invoke a getter or a setter from a null bean");
	}

	private void checkPropertyName(String property) throws ReflectionException {
		if (property == null || property.length() == 0)
			throw new ReflectionException(
					"The property name cannot be null or empty");
	}

	/**
	 * Invoke the property getter on the bean
	 * 
	 * @param bean
	 * @param property
	 * @return
	 */
	protected abstract Object invokeGetter(B bean, String property)
			throws ReflectionException;

	/**
	 * Invoke the property setter on the bean
	 * 
	 * @param bean
	 * @param property
	 * @param value
	 */
	protected abstract void invokeSetter(B bean, String property, Object value)
			throws ReflectionException;

}
